package handler

import (
	"errors"
	"fmt"
	"mime/multipart"
	"net/http"
	"net/url"
	"path/filepath"
	"ruoyi-go-echo-ddd/internal/domain/model"
	"ruoyi-go-echo-ddd/internal/infrastructure/http/response"
	"ruoyi-go-echo-ddd/internal/infrastructure/http/validator"
	"ruoyi-go-echo-ddd/internal/infrastructure/utils"
	"strconv"
	"strings"

	"github.com/asaskevich/govalidator"
	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog"
)

// BindParam 获取路径，查询，及请求体参数到结构体到指针中
func BindParam[T any](c echo.Context, param *T) error {
	if err := c.Bind(param); err != nil {
		return err
	}
	return nil
}

// BindValidate 获取参数并且进行验证
func BindValidate[T any](c echo.Context, message string, data *T) error {
	if err := c.Bind(data); err != nil {
		return fmt.Errorf(message, err)
	}
	if err := validator.Validate.Struct(data); err != nil {
		return fmt.Errorf(message, err)
	}
	return nil
}

// HandleError 处理错误
func HandleError(c echo.Context, statusCode int, logMsg, message string, err error) error {
	zerolog.DefaultContextLogger.Error().Err(err).Msg(logMsg)
	response.NewRespCodeMsg(c, statusCode, message)
	return err
}

// RegisterCustomValidator 注册自定义验证器
func RegisterCustomValidator(key string, value govalidator.CustomTypeValidator) {
	if key == "" || value == nil {
		return
	}
	govalidator.CustomTypeTagMap.Set(key, value)
}

// RegisterCustomValidators 注册自定义验证器
func RegisterCustomValidators(validatorMap map[string]govalidator.CustomTypeValidator) {
	if len(validatorMap) == 0 {
		return
	}
	for key, value := range validatorMap {
		RegisterCustomValidator(key, value)
	}
}

// ValidatorStruct 验证结构体
func ValidatorStruct(t any) error {
	_, err := govalidator.ValidateStruct(t)
	if err != nil {
		if validatorErrors, ok := err.(govalidator.Errors); ok {
			var errList []string
			for _, e := range validatorErrors {
				errList = append(errList, e.Error())
			}
			return errors.New(strings.Join(errList, ";"))
		}
	}
	return nil
}

// UseCustomValidator 单个验证
//
// 参数
//
//	-t: 泛型结构体
//	-validatorMap: 名称
//	-e: echo.Context echo上下文
//
// 返回
//
//	-error: 错误
func UseCustomValidator[T any](t T, validatorMap map[string]govalidator.CustomTypeValidator, e echo.Context) error {
	RegisterCustomValidators(validatorMap)
	if err := ValidatorStruct(t); err != nil {
		response.NewRespCodeMsg(e, http.StatusInternalServerError, err.Error())
		return errors.New("校验失败")
	}
	return nil
}

func LoginValidator[T any](t T, validatorMap map[string]govalidator.CustomTypeValidator) (bool, error) {
	RegisterCustomValidators(validatorMap)
	if err := ValidatorStruct(t); err != nil {
		return false, errors.New("校验失败")
	}
	return true, nil
}

// UseCustomValidators 切片验证
//
// 参数
//
//	-list: 泛型切片
//	-validatorMap: 名称
//	-e: echo.Context echo上下文
//	-duplicate: 是否去除重复验证信息
//
// 返回
//
//	-error: 错误
func UseCustomValidators[T any](list []T, validatorMap map[string]govalidator.CustomTypeValidator, e echo.Context, duplicate bool) error {
	if len(list) <= 0 {
		return errors.New("验证数据不能为空")
	}

	RegisterCustomValidators(validatorMap)
	// 错误切片
	var errList []string
	for _, t := range list {
		_, err := govalidator.ValidateStruct(t)
		if err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Msg("结构体验证错误")
			s := err.Error()
			split := strings.Split(s, ";")
			if len(split) <= 1 {
				errList = append(errList, s)
			} else {
				errList = append(errList, split...)
			}
		}
	}

	zerolog.DefaultContextLogger.Info().Msgf("原始验证错误信息===%+v", errList)

	if len(errList) > 0 {
		if duplicate {
			errList = utils.DeduplicateStrings(errList)
		}
		zerolog.DefaultContextLogger.Info().Msgf("结构体校验失败,错误切片==%+v", errList)
		response.NewRespCodeMsg(e, http.StatusInternalServerError, strings.Join(errList, ";"))
		return errors.New("校验失败")
	}
	return nil
}

// GetSinglePathParam 获取单个restful请求path参数
func GetSinglePathParam(c echo.Context, paramName string) (string, error) {
	value := c.Param(paramName)
	if value == "" {
		zerolog.DefaultContextLogger.Info().Msgf("获取PATH单个参数==%s为空", paramName)
		return "", errors.New("参数验证失败")
	}

	return value, nil
}

// GlobalDeleteHandler 全局删除处理器
func GlobalDeleteHandler(c echo.Context, paramStr string) ([]any, error) {
	if paramStr == "" {
		paramStr = "id"
	}
	param := c.Param(paramStr)
	if param == "" {
		return nil, errors.New("参数验证失败")
	}

	decodedParam, err := url.QueryUnescape(param)
	if err != nil {
		return nil, fmt.Errorf("参数解码失败: %w", err)
	}

	ids := strings.Split(decodedParam, ",")
	if len(ids) == 0 {
		return nil, errors.New("参数验证失败")
	}

	return utils.StringSliceToAnySlice(ids), nil
}

func DownloadExcelBuffer[T any](c echo.Context, fileName, sheetName string, headers []string, data []T, headerColor bool) error {
	zerolog.DefaultContextLogger.Info().Msg("下载excel方法被调用了")
	excelBuffer, err := utils.WriteDataToExcelBufferProcess(headers, data, sheetName, headerColor)
	if err != nil {
		response.NewRespCodeErr(c, 500, err)
		return err
	}

	SetDownloadRespOption(c, int64(excelBuffer.Len()), fileName)

	// 将文件缓冲写入echo响应中
	_, err = excelBuffer.WriteTo(c.Response().Writer)
	if err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msgf("excel===%s写入响应错误", fileName)
		response.NewRespCodeErr(c, 500, err)
		return err
	}
	return nil
}

// SetDownloadRespOption 设置下载参数
func SetDownloadRespOption(c echo.Context, fileSize int64, fileName string) {
	zerolog.DefaultContextLogger.Info().Msg("设置excel响应方法被调用了")
	header := c.Response().Header()
	// 设置相应内容类型
	header.Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	// 设置响应内容大小
	header.Set("Content-Length", strconv.FormatInt(fileSize, 10))

	// 根据浏览器类型优化
	userAgent := c.Request().UserAgent()
	zerolog.DefaultContextLogger.Info().Str("userAgent", userAgent).Msg("设置excel响应获取userAgent信息")

	// 文件名处理（现代浏览器兼容方案），防止路径遍历攻击，兼容中文文件名，解决中文乱码问题
	encodedName := url.PathEscape(filepath.Base(fileName))

	// 统一设置Content-Disposition，使用双保险策略
	contentDisp := fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, encodedName, encodedName)
	zerolog.DefaultContextLogger.Info().Msgf("设置excel响应使用双保险模式: %s", contentDisp)

	/* switch {
	   case strings.Contains(userAgent, "Safari") && !strings.Contains(userAgent, "Chrome"):
	   	// Safari 浏览器特殊处理
	   	contentDisp = fmt.Sprintf(`attachment; filename="%s"`, encodedName)
	   	zerolog.DefaultContextLogger.Info().Str("type", "safari").Msg("设置excel响应使用Safari兼容模式")
	   default:
	   	// Chrome/Firefox/Edge 等现代浏览器
	   	contentDisp = fmt.Sprintf(`attachment; filename*=UTF-8''%s`, encodedName)
	   	zerolog.DefaultContextLogger.Info().Str("type", "standard").Msg("设置excel响应使用标准模式")
	   } */

	header.Set("Content-Disposition", contentDisp)
	// 缓存控制
	header.Set("Cache-Control", "no-store, must-revalidate")
	header.Set("Pragma", "no-cache")
	header.Set("Expires", "0")
}

// GetEchoForm 获取表单
func GetEchoForm(c echo.Context, maxMemory int64) (*multipart.Form, error) {
	// 解析表单，设置表单内存缓存大小
	if err := c.Request().ParseMultipartForm(maxMemory); err != nil {
		return nil, err
	}
	return c.Request().MultipartForm, nil
}

func VerifyRoleAllowed(role *model.SysRole) bool {
	if role == nil {
		return true
	}

	if role.Id != 0 && role.Id == int64(1) {
		return false
	}
	return true
}
